<?php
/**
* Root class for webappkit storages
*
* @link http://webappkit.net
* @package webappkit
* @author J.Ducastel <jeremie@ducastel.name>
* @license http://opensource.org/licenses/gpl-license.php GNU Public License
*/

/**
* Root class for webappkit object instances storages.
* generic filesystem-based storage.
* Use "path template" to retrieve a file by search crieria,
* then build an object instance from the file
* @author J.Ducastel <jeremie@ducastel.name>
* @todo criteria validation (regex ?)
*/
class wakStorage {
/*--------------------------------------------------------------------
CONSTRUCTOR
--------------------------------------------------------------------*/
	/**
	* php4 constructor
	* @access public
	*/
	function wakStorage(&$kit,$name) {
		$this->__construct($kit,$name);
	}/**/
	
	/**
	* "real" constructor.
	* will load conf
	* @access public
	* @param webappkit $kit parent kit's reference
	* @param string $name storage name within kit
	*/
	function __construct(&$kit,$name) {
		$this->kit=&$kit;
		$this->name=$name;
		// loading config
		$this->_loadIni();
		// doing some more initialization
		$this->_init();
	}/**/
/*--------------------------------------------------------------------
PUBLIC INSTANCE METHODS
--------------------------------------------------------------------*/
	/**
	* search and returns a single item from store, if found
	* @param array|string $criteria can be either a single criterium ($id)
	* or an associative array
	* @access public
	* @return object
	*/
	function getItem($criteria) {
		// 
		return $this->_getItem($criteria);
	}/**/
	
	/**
	* sets new default value for a criteria
	* @param string $key
	* @param string $value
	* @access public
	* @return bool
	*/
	function setCriteria($key,$value) {
		$this->criteria[$key]=$value;
		return true;
	}/**/

	/**
	*
	* @param
	* @access public
	* @return
	* /
	function () {

	}/**/
/*--------------------------------------------------------------------
VARS / MEMBERS
--------------------------------------------------------------------*/
	/**
	* @var webappkit parent kit reference
	*/
	var $kit;
	
	/**
	* @var string self unique name within kit
	*/
	var $name;
	
	/**
	* @var array configuration data
	*/
	// var $cfg;
	
	/**
	 * @var string search template
	 * criteria to replace have to be between accolads like
	 * {optional_part{criteria_name}optional_part}
	 **/
	var $search='{{id}}.txt';
	
	/**
	 * @var string objects class name
	 **/
	var $stored_class;
	
	/**
	 * @var array default values for criteria
	 **/
	var $criteria=array();
	
	/**
	 * @var array object instances cache
	 */
	var $objects=array();
/*--------------------------------------------------------------------
PRIVATE / PROTECTED METHODS
--------------------------------------------------------------------*/
	/**
	* loads config ini file ( {name}.storage.ini )
	* @access protected
	* @return bool
	*/
	function _loadIni() {
		// getting ini file
		$file=$this->kit->getPath().$this->name.'.storage.ini'; //echo $file;
		if (!file_exists($file) or !is_file($file)) {
			// trigger_error("no config file for storage ($file)");
			return false;
		}
		// parsing, with sections
		if (!$cfg=parse_ini_file($file,true)) {
			trigger_error("failed to parse $file");
			return false;
		}
		$this->cfg=&$cfg;
		return true;
	}/**/
	
	/**
	* storage intialization
	* @access protected
	* @return bool
	*/
	function _init() {
		// importing config
		if (isset($this->cfg['class']))
			$this->stored_class=$this->cfg['class'];
		if (isset($this->cfg['search']))
			$this->search=$this->cfg['search'];
		if (isset($this->cfg['criteria']))
			$this->criteria=$this->cfg['criteria'];
		return true;
	}/**/
	
	/**
	 * search, get and builds and item from passed criteria
	 * @access protected
	 * @param array|string $criteria
	 * @return object|null
	 */
	function & _getItem($criteria) {
		// filling criteria with defaults
		$criteria=$this->_fillCriteria($criteria);
		// building unique instance id from criteria
		$id=serialize($criteria);
		// searching into objects cache, building if required
		if (!isset($this->objects[$id])) {
			// searching filename
			if (!$filename=$this->_searchFile($criteria)) {
				//trigger_error("item $id not found in storage {$this->name} from kit {$this->kit->id}");
				$n=null; return $n;
			}
			// building
			$this->objects[$id]=&$this->_factory($filename);
		}
		// returning instance
		return $this->objects[$id];
	}/**/
	
	/**
	 * computes unique string id from criteria.
	 * This is used as key for stored object references
	 * /
	 function _footPrint($criteria) {
		$str=join('',array_keys($criteria)).
	 }/**/
	
	/**
	 * Merge passed search criteria with defaults
	 * @access protected
	 * @param array $criteria associative array
	 * @return array
	 */
	function _fillCriteria($criteria) {
		// string criteria = id
		if (is_string($criteria)) {
			$criteria=array('id'=>$criteria);
		}
		// looping defaults
		foreach ($this->criteria as $key => $value) {
			// setting default if not set in arg
			if (!isset($criteria[$key]))
				$criteria[$key]=$value;
		}
		return $criteria;
	}/**/
	
	/**
	 * injects criteria into search template to get filename.
	 * checks that file exists.
	 * @access protected
	 * @param array $criteria
	 * @return string filename
	 */
	function _searchFile($criteria) {
		$n=0;
		$regex='\{([^{}]*)\{([a-zA-Z][a-zA-Z0-9_\.-]*)\}([^{}]*)\}';
		// echo $regex;
		$filename=$this->search;
		while (ereg($regex,$filename,$found)) {
			// avoiding infinite loop
			$n++; if ($n>50) break;
			// print_r($found);
			$before=$found[1];
			$key=$found[2];
			//$force=$found[3];
			$after=$found[3];
			// building replacement block
			if (isset($criteria[$key])
			and is_string($criteria[$key])
			and $criteria[$key]) {
				// provided, not empty string value
				$replace=$before.$criteria[$key].$after;
			} else {
				// not provided or null value
				$replace='';
			}
			// replacing
			// escaping regex special chars
			$before=str_replace('(','\(',$before);
			$before=str_replace(')','\)',$before);
			$after=str_replace('(','\(',$after);
			$after=str_replace(')','\)',$after);
			// replacing regex
			$ereg2='\{'.$before.'\{'.$key.'\}'.$after.'\}';
			$filename=ereg_replace($ereg2,$replace,$filename);
		}
		// adding kit path
		$filename=$this->kit->getPath().$filename;
		// echo "<p>filename=$filename</p>";
		if (!file_exists($filename))
			return false;
		return $filename;
	}/**/

	/**
	* builds an object instance from the file name
	* @param string $filename
	* @access protected
	* @return object
	*/
	function & _factory($filename) {
		// reading file content
		$content=join('',file($filename));
		// getting class
		$class=$this->stored_class;
		// passing file content to constructor
		return new $class($content);
	}/**/
}
?>